<?php
class GPG {

	private $passphrase;
	private $identity;
	private $gpgpath = "/usr/bin/gpg";
	private $gpghome = "/home/rsigler/.gnupg";
	
	const ENCRYPT = 1;
	const DECRYPT = 2;
		
	function __construct($options = null) {
		if (is_array($options)) {
			if (isset($options["identity"])) {
				$this->setID($options["identity"]);
			}
			if (isset($options["passphrase"])) {
				$this->setPassphrase($options["passphrase"]);
			}
			if (isset($options["gpgpath"])) {
				$this->gpgpath($options["gpgpath"]);
			}
			if (isset($options["gpghome"])) {
				$this->gpghome($options["gpghome"]);
			}
		}
		return true;
	}
	
	public function setID($id) {
		$this->identity = $id;
		return true;
	}
	
	public function setPassphrase($pass) {
		$this->passphrase = $pass;
		return true;
	}
	
	public function gpgpath($gpgpath) {
		$this->gpgpath = $gpgpath;
		return true;
	}
	
	public function gpghome($gpghome) {
		$this->gpghome = $gpghome;
		return true;
	}
	
	private function sanityCheck() {
		if (!is_executable($this->gpgpath)) {
			throw new Exception (Errors::GPG_MESG_CANT_EXEC_GPG, Errors::GPG_CODE_CANT_EXEC_GPG);
		}
		
		if (!is_dir($this->gpghome)) {
			throw new Exception (Errors::GPG_MESG_HOME_NOT_DIR, Errors::GPG_CODE_HOME_NOT_DIR);
		}
		
		if (is_null($this->identity)) {
			throw new Exception (Errors::GPG_MESG_IDENTITY_NOT_SET, Errors::GPG_CODE_IDENTITY_NOT_SET);
		}
		
		if (is_null($this->passphrase)) {
			throw new Exception (Errors::GPG_MESG_PASSPHRASE_NOT_SET, Errors::GPG_CODE_PASSPHRASE_NOT_SET);
		}
		
		return true;
	}
	
	public function encrypt($input, $output, $iSize) {
		return $this->crypter($input, $output, $iSize, self::ENCRYPT);
	}
	
	public function decrypt($input, $output, $iSize) {
		return $this->crypter($input, $output, $iSize, self::DECRYPT);
	}
	
	public function crypter($input, $output, $iSize, $mode) {
		$this->sanityCheck();
		
		if ($mode == self::ENCRYPT) {
			$cmd = $this->gpgpath . " --homedir " . $this->gpghome . " --quiet --batch -r " . $this->identity . " --passphrase-fd 3 --encrypt --sign -";
		} elseif ($mode == self::DECRYPT) {
			$cmd = $this->gpgpath . " --homedir " . $this->gpghome . " --quiet --batch --passphrase-fd 3 -";
		} else {
			return false;
		}
		echo "I will now de/en-code!\n";
		$descriptors = array(
			0 => array("pipe", "r"), // process input (stdin)
			1 => array("pipe", "w"), // get data from process (stdout)
			2 => array("pipe", "w"), // stderr
			3 => array("pipe", "r")  // Passphrase pipe
		);
		
		$ps = proc_open($cmd, $descriptors, $pipes);
		stream_set_blocking($pipes[0], 0);
		stream_set_blocking($pipes[1], 0);
		stream_set_blocking($pipes[2], 0);
		stream_set_blocking($pipes[3], 0);

		if (is_resource($ps)) {
			// write out passphrase
			fwrite($pipes[3], $this->passphrase);
			fclose($pipes[3]);
			
			$txOff = 0;
			$txLen = $iSize;
			$stdout = "";
			$stdoutDone = FALSE; 
			$stderr = "";
			$stderrDone = FALSE; 
			if ($txLen == 0) {
				fclose($pipes[0]);
			}
			while (TRUE) {
				echo ".";
				$rx = array(); // The program's stdout/stderr 
				if (!$stdoutDone) {
					$rx[] = $pipes[1];
				} 
				if (!$stderrDone) {
					$rx[] = $pipes[2];
				} 
				$tx = array(); // The program's stdin 
				if ($txOff < $txLen) {
					$tx[] = $pipes[0];
				} 
				stream_select($rx, $tx, $ex = NULL, NULL, NULL); // Block til r/w possible 
				if (!empty($tx)) { 
					$txRet = fwrite($pipes[0], stream_get_contents($input, 8192, $txOff));
					fflush($pipes[0]);
					if ($txRet !== FALSE) {
						$txOff += $txRet;
					} 
					if ($txOff >= $txLen) {
						fclose($pipes[0]);
					} 
				} 
				foreach ($rx as $r) { 
					if ($r == $pipes[1]) {
						echo ",";
						fwrite($output, fread($pipes[1], 8192));
						fflush($output); 
						if (feof($pipes[1])) {
							fclose($pipes[1]);
							$stdoutDone = TRUE;
						} 
					} else if ($r == $pipes[2]) { 
						$stderr .= fread($pipes[2], 8192); 
						if (feof($pipes[2])) {
							fclose($pipes[2]); 
							$stderrDone = TRUE;
						} 
					} 
				} 
				if (!is_resource($ps)) {
					break;
				} 
				if ($txOff >= $txLen && $stdoutDone && $stderrDone) {
					break;
				} 
			} 
			$returnValue = proc_close($ps);  
		}
		return true;
	}
	
	function str2hex($string) {
		$hstr = "";
		for ($i = 0; $i < strlen($string); $i++) {
			$hstr .= " " . bin2hex($string[$i]);
		}
		return $hstr;
	}
}
?>